﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.IO;
using System.Net;
using System.Threading;
using KonachanChanger.Data;
using KonachanChanger.Shell;

namespace KonachanChanger.API
{
    public class post
    {
        [XmlAttribute]
        public int width;
        [XmlAttribute]
        public int height;
        [XmlAttribute]
        public string tags;
        [XmlAttribute]
        public string rating;
        [XmlAttribute]
        public string jpeg_url;
        [XmlAttribute]
        public string status;
        [XmlAttribute]
        public string file_url;
        [XmlAttribute]
        public string md5;
        [XmlAttribute]
        public int score;
        [XmlAttribute]
        public string preview_url;
        [XmlAttribute]
        public int preview_height;
        [XmlAttribute]
        public int preview_width;
        [XmlAttribute]
        public bool has_children;
        [XmlAttribute]
        public int id;

        public string Rating
        {
            get
            {
                string r = LocalDb.GetInstance().CustomRating(this.id);
                if (r != null)
                    return r;
                return rating;
            }
        }
        //flag_detail="" 
        //jpeg_file_size="0" 
        //sample_width="1500" 
        //is_held="false" 
        //actual_preview_height="225" 
        //source="" 
        //sample_height="1125" 
        //sample_file_size="1177778" 
        //sample_url="http://konachan.com/sample/07b3071223978dd2d2f5a1894503877e/Konachan.com%20-%20106268%20sample.jpg" 
        //frames_pending_string="" 
        //file_size="500657" 
        //is_shown_in_index="true" 
        //change="331833" 
        //parent_id="" 
        //jpeg_width="2048" 
        //creator_id="14063" 
        //actual_preview_width="300" 
        //author="meccrain" 
        //jpeg_height="1536" 
	    //created_at="1307384986" 
        
	    //frames_string="" 

        public string[] Tags
        {
            get
            {
                
                var remote = tags.ToLower().Split(' ');
                var local = LocalDb.GetInstance().CustomTags(this.id);
                return remote;
            }
        }

        public bool Blocked
        {
            get
            {
                return LocalDb.GetInstance().BlockedIds.Contains(this.id);
            }
        }

        public void BlockPost()
        {
            LocalDb.GetInstance().BlockedIds.Add(this.id);
        }

        public string Download()
        {
            if (file_url == null)
                throw new InvalidDataException("No download path?");
            string path = SearchFile(false);
            if (File.Exists(path))
                return path;
            if (UserPrefs.GetInstance().EnableP2P)
            {
                Extensions.P2P.P2PCore.Instance.Request(this);
            }
            path = Path.Combine(Program.SavePath(rating), md5 + Path.GetExtension(file_url));
            Program.Log("Downloading {0} to {1}", this.id, path);
            WebClient wc = new WebClient();
            var b = wc.DownloadData(file_url);
            File.WriteAllBytes(path, b);
            return path;
        }

        public string SearchFile(bool isThumb)
        {
            foreach (var SavePath in new string[] { LocalDb.GetInstance().Prefs.SavePathS, LocalDb.GetInstance().Prefs.SavePathQ, LocalDb.GetInstance().Prefs.SavePathE })
            {
                string path = Path.Combine(SavePath, md5 + Path.GetExtension(file_url));
                if (isThumb)
                {
                    path = Path.Combine(SavePath, ".thumbnails", md5 + Path.GetExtension(preview_url));
                    if (File.Exists(path))
                        return path;
                }
                else
                {
                    foreach (var dir in Directory.EnumerateDirectories(SavePath))
                    {
                        string d2 = dir;
                        if (dir.EndsWith(".thumbnails"))
                            d2 = SavePath;
                        if (File.Exists(path = Path.Combine(d2, md5 + Path.GetExtension(file_url))))
                        {
                            string hash;
                            try
                            {
                                hash = MD5Func.GetMd5HashFromFile(path);
                            }
                            catch (IOException)
                            {
                                throw new InvalidDataException("File is already Downloading.");
                            }
                            if (hash == md5)
                                return path;
                            else
                                File.Delete(path);
                        }
                    }
                }
            }
            //Could not find.  Give default values.
            if (isThumb)
                return Path.Combine(Program.SavePath(this.Rating), ".thumbnails", md5 + Path.GetExtension(preview_url));
            else
                return Path.Combine(Program.SavePath(this.Rating), md5 + Path.GetExtension(file_url));
        }

        public void SetWallpaper()
        {
            WallpaperWrapper.SetPostAsWallPaper(this);
        }

        public void OpenPage()
        {
            try
            {
                System.Diagnostics.Process.Start(@"http://konachan.com/post/show/" + id);
            }
            catch
            {
                System.Windows.Forms.MessageBox.Show("Unable to open browser.");
            }
        }
        
        /// <summary>
        /// Returns the location of a thumbnail.  May or may not be local.
        /// </summary>
        /// <returns></returns>
        public string ThumbnailPath()
        {
            string img = ThumbnailPathLocal();
            if (!File.Exists(img))
            {
                ThreadPool.QueueUserWorkItem((e) =>
                {
                    try
                    {
                        var content = new WebClient().DownloadData(new Uri(this.preview_url));
                        File.WriteAllBytes(img, content);
                    }
                    catch (WebException)
                    {

                    }
                });
                return preview_url; // Then, we tell the UI to reference the website directly this time.  This is because we can't delay this method until the image is downloaded.
            }
            return img;
        }

        public string ImagePath
        {
            get
            {
                var localdb = LocalDb.GetInstance();
                switch (Rating)
                {
                    case "s":
                        return Path.Combine(localdb.Prefs.SavePathS, md5 + FileExtension);
                    case "q":
                        return Path.Combine(localdb.Prefs.SavePathQ, md5 + FileExtension);
                    case "e":
                        return Path.Combine(localdb.Prefs.SavePathS, md5 + FileExtension);
                    default:
                        return Path.Combine(localdb.Prefs.SavePathS, md5 + FileExtension);

                }
                
            }
        }
        public override bool Equals(object obj)
        {
            if (obj is post)
                return (obj as post).id == this.id;
            return base.Equals(obj);
        }

        public override int GetHashCode()
        {
            return this.id;
        }

        internal string ThumbnailPathLocal()
        {
            string ext = Path.GetExtension(this.preview_url);
            string img;
            if (File.Exists(img = Path.Combine(".thumbnails", String.Concat(this.md5, ext))))
                return img;
            if (File.Exists(img = Path.Combine(".thumbnails", String.Concat(this.md5, ".", ext))))
            {
                File.Move(img, img = img.Replace("." + ext, ext));
                return img;
            }
            if (File.Exists(img = Path.Combine(".thumbnails", this.rating, String.Concat(this.md5, ext))))
                return img;
            if (File.Exists(SearchFile(true)))
                return img;
            return img;
        }

        public string FileExtension { get { return Path.GetExtension(file_url); } }

        public string FavoriteTag
        {
            get
            {
                List<string> opt = new List<string>();
                foreach (var tag in Tags)
                {
                    if (LocalDb.GetInstance().FavoriteTags.Contains(tag))
                        opt.Add(tag);
                }
                try
                {
                    opt.Sort(FavTagSorter);
                }
                catch (WebException) { }
                catch (InvalidOperationException) { }
                return opt.FirstOrDefault();
            }
        }

        private int FavTagSorter(string x, string y)
        {
            var local = LocalDb.GetInstance();
            TagCollection.TagInfo xd;
            if (!local.Tags.TryGetValue(x, out xd))
                xd = TagCollection.LoadTagInfo(x).First(e => e.Name == x);
            TagCollection.TagInfo yd;
            if (!local.Tags.TryGetValue(y, out yd))
                xd = TagCollection.LoadTagInfo(y).First(e => e.Name == y);
            var tdiff = xd.Type - yd.Type;
            if (tdiff != 0)
                return tdiff;
            return xd.Count - yd.Count;
        }

        public bool Downloaded
        {
            get
            {
                if (file_url == null)
                    return false;
                string path = SearchFile(false);
                return File.Exists(path);
            }
        }
    }

    public class posts
    {
        [XmlElement(typeof(post)), XmlElement(typeof(string))]
        public object[] Posts;

        [XmlAttribute]
        public int count;

        [XmlAttribute]
        public int offset;

        public bool Finished = false;

        public void Union(posts more)
        {
            if (more.Posts == null)
            {
                Finished = true;
                return;
            }
            Posts = Posts.Union(more.Posts).ToArray();
        }

        public post RandomPost
        {
            get
            {
                if (Posts == null || Posts.Length == 0)
                    return null;
                return Posts[new Random().Next(Posts.Length)] as post;
            }
        }

        public IEnumerable<post> CastedPosts()
        {
            return Posts.ToArray().Select((x) => x as post);
        }

        private post _next = null;
        public post NextRandomWallpaper
        {
            get
            {
                return _next;
            }
            set
            {
                _next = value;
                if (value == null)
                {
                    // Pick one!
                    bool allowMultiSearchThreads = true;
                    if (Picking == null || !Picking.IsAlive || allowMultiSearchThreads)
                    {
                        Picking = new Thread(new ThreadStart(PickAPaper)) { IsBackground = true, Name = "Picking!" };
                        Picking.Start();
                    }
                }
            }
        }

        private void PickAPaper()
        {
            while (_next == null)
            {
                try
                {
                    var potential = this.RandomPost;
                    if (potential == null)
                        continue;
                    var file = potential.Download();
                    if (File.Exists(file))
                    {
                        NextRandomWallpaper = potential;
                    }
                }
                catch (WebException)
                { }
                catch (InvalidDataException)
                { }
            }
        }
        Thread Picking = null;
    }
}
